﻿using System;

namespace NodeJS.net
{
    using NodeJS.stream;

    /// <summary>
    /// This object is an abstraction of a TCP or UNIX socket.
    /// They can be created by the user and used as a client (with connect()) or they can be created by Node and passed to the user through the 'connection' event of a server.
    /// </summary>
    public class Socket : Duplex
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="options">options is an object with the following:
        /// <para>fd allows you to specify the existing file descriptor of socket.</para>
        /// <para>type specified underlying protocol. It can be 'tcp4', 'tcp6', or 'unix'.</para>
        /// <para>allowHalfOpen, refer to createServer() and 'end' event.</para>
        /// default is { fd: null, type: null, allowHalfOpen: false }
        /// </param>
        public Socket(object options = null)
        {
        }

        /// <summary>
        /// Opens the connection for a given socket. Opened as a TCP socket, if host is omitted, localhost will be assumed.
        /// Normally this method is not needed, as net.createConnection opens the socket. Use this only if you are implementing a custom Socket.
        /// </summary>
        /// <param name="port"></param>
        /// <param name="host"></param>
        /// <param name="connectionListener"></param>
        public void Connect(int port, string host = "localhost", Action connectionListener = null)
        {
        }

        /// <summary>
        /// Opens the connection for a given socket. The socket will be opened as a unix socket to that path.
        /// Normally this method is not needed, as net.createConnection opens the socket. Use this only if you are implementing a custom Socket.
        /// </summary>
        /// <param name="path"></param>
        /// <param name="connectionListener"></param>
        public void Connect(string path, Action connectionListener = null)
        {
        }

        /// <summary>
        /// This property shows the number of characters currently buffered to be written. (Number of characters is approximately equal to the number of bytes to be written, but the buffer may contain strings, and the strings are lazily encoded, so the exact number of bytes is not known.)
        /// </summary>
        public long BufferSize;

        /// <summary>
        /// Half-closes the socket. i.e., it sends a FIN packet. It is possible the server will still send some data.
        /// If data is specified, it is equivalent to calling socket.write(data, encoding) followed by socket.end().
        /// </summary>
        /// <param name="chunk"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public bool End(StringOrBuffer chunk = null, string encoding = "")
        {
            return false;
        }

        /// <summary>
        /// Ensures that no more I/O activity happens on this socket. Only necessary in case of errors (parse error or so).
        /// </summary>
        public void Destroy()
        {
        }

        /// <summary>
        /// Sets the socket to timeout after timeout milliseconds of inactivity on the socket. By default net.Socket do not have a timeout.
        /// When an idle timeout is triggered the socket will receive a 'timeout' event but the connection will not be severed. The user must manually end() or destroy() the socket.
        /// </summary>
        /// <param name="timeout">milliseconds. If timeout is 0, then the existing idle timeout is disabled.</param>
        /// <param name="callback">The callback parameter will be added as a one time listener for the 'timeout' event.</param>
        public void SetTimeout(long timeout, Action callback = null)
        {
        }

        /// <summary>
        /// Disables the Nagle algorithm. By default TCP connections use the Nagle algorithm, they buffer data before sending it off.
        /// Setting true for noDelay will immediately fire off data each time socket.write() is called.
        /// </summary>
        /// <param name="noDelay">default to true</param>
        public void SetNoDelay(bool noDelay = true)
        {
        }

        /// <summary>
        /// Enable/disable keep-alive functionality, and optionally set the initial delay before the first keepalive probe is sent on an idle socket. 
        /// </summary>
        /// <param name="enable"></param>
        /// <param name="initialDelay">Set initialDelay (in milliseconds) to set the delay between the last data packet received and the first keepalive probe.
        /// <para>Setting 0 for initialDelay will leave the value unchanged from the default (or previous) setting. Defaults to 0.</para>
        /// </param>
        public void SetKeepAlive(bool enable = false, long initialDelay = 0)
        {
        }

        /// <summary>
        /// Returns the bound address, the address family name and port of the socket as reported by the operating system. 
        /// </summary>
        /// <returns>Returns an object with three properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' }</returns>
        public virtual SocketAddress Address()
        {
            return null;
        }

        /// <summary>
        /// Calling unref on a socket will allow the program to exit if this is the only active socket in the event system.
        /// If the socket is already unrefd calling unref again will have no effect.
        /// </summary>
        public void Unref()
        {
        }

        /// <summary>
        /// Opposite of unref, calling ref on a previously unrefd socket will not let the program exit if it's the only socket left (the default behavior).
        /// If the socket is refd calling ref again will have no effect.
        /// </summary>
        public void Ref()
        {
        }

        /// <summary>
        /// The string representation of the remote IP address. For example, '74.125.127.100' or '2001:4860:a005::68'.
        /// </summary>
        public string RemoteAddress;

        /// <summary>
        /// The numeric representation of the remote port. For example, 80 or 21.
        /// </summary>
        public int RemotePort;

        /// <summary>
        /// The string representation of the local IP address the remote client is connecting on. For example, if you are listening on '0.0.0.0' and the client connects on '192.168.1.1', the value would be '192.168.1.1'.
        /// </summary>
        public string LocalAddress;

        /// <summary>
        /// The numeric representation of the local port. For example, 80 or 21.
        /// </summary>
        public int LocalPort;

        /// <summary>
        /// The amount of received bytes.
        /// </summary>
        public long BytesRead;

        /// <summary>
        /// The amount of bytes sent.
        /// </summary>
        public long BytesWritten;

        /// <summary>
        /// Emitted when a socket connection is successfully established. See connect().
        /// </summary>
        [EvalAtCompile(Value = "connect")]
        public const NodeEvent CONNECT = null;

        /// <summary>
        /// Emitted when data is received. Encoding of data is set by socket.setEncoding(). (See the Readable Stream section for more information.)
        /// Note that the data will be lost if there is no listener when a Socket emits a 'data' event.
        /// </summary>
        [EvalAtCompile(Value = "data")]
        public const NodeEvent<StringOrBuffer> DATA = null;


        /// <summary>
        /// Emitted when the other end of the socket sends a FIN packet.
        /// By default (allowHalfOpen == false) the socket will destroy its file descriptor once it has written out its pending write queue. However, by setting allowHalfOpen == true the socket will not automatically end() its side allowing the user to write arbitrary amounts of data, with the caveat that the user is required to end() their side now.
        /// </summary>
        [EvalAtCompile(Value = "end")]
        public const NodeEvent END = null;

        /// <summary>
        /// Emitted if the socket times out from inactivity. This is only to notify that the socket has been idle. The user must manually close the connection.
        /// </summary>
        [EvalAtCompile(Value = "timeout")]
        public const NodeEvent TIMEOUT = null;

        /// <summary>
        /// Emitted when the write buffer becomes empty. Can be used to throttle uploads.
        /// </summary>
        [EvalAtCompile(Value = "drain")]
        public const NodeEvent DRAIN = null;

        /// <summary>
        /// Emitted when an error occurs. The 'close' event will be called directly following this event.
        /// </summary>
        [EvalAtCompile(Value = "error")]
        public const NodeEvent<Exception> ERROR = null;

        /// <summary>
        /// Emitted once the socket is fully closed. The argument had_error is a boolean which says if the socket was closed due to a transmission error.        
        /// </summary>
        [EvalAtCompile(Value = "close")]
        public const NodeEvent<bool> CLOSE = null;
    }
}
